package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;
import com.lkd.contract.TaskCompleteContract;
import com.lkd.dao.TaskDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.TaskDetailsEntity;
import com.lkd.entity.TaskEntity;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.http.vo.CancelTaskViewModel;
import com.lkd.http.vo.TaskDetailsViewModel;
import com.lkd.http.vo.TaskViewModel;
import com.lkd.service.TaskDetailsService;
import com.lkd.service.TaskService;
import com.lkd.vo.Pager;
import com.lkd.vo.UserVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.security.auth.login.LoginException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskDao,TaskEntity> implements TaskService{

    @Autowired
    private UserService userService;

    @Autowired
    private VMService vmService;

    @Autowired
    private TaskDetailsService taskDetailsService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private MqttProducer mqttProducer;

    @Override
    public Pager<TaskEntity> search(Long pageIndex, Long pageSize, String innerCode, Integer userId, String taskCode, Integer status, Boolean isRepair, String start, String end) {
        Page<TaskEntity> page = new Page<>(pageIndex,pageSize);
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        if(!Strings.isNullOrEmpty(innerCode)){
            qw.eq(TaskEntity::getInnerCode,innerCode);
        }
        if(userId != null && userId > 0){
            qw.eq(TaskEntity::getUserId,userId);
        }
        if(!Strings.isNullOrEmpty(taskCode)){
            qw.like(TaskEntity::getTaskCode,taskCode);
        }
        if(status != null && status > 0){
            qw.eq(TaskEntity::getTaskStatus,status);
        }
        if(isRepair != null){
            if(isRepair){
                qw.ne(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
            }else {
                qw.eq(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
            }
        }
        if(!Strings.isNullOrEmpty(start) && !Strings.isNullOrEmpty(end)){
            qw
                    .ge(TaskEntity::getCreateTime,LocalDate.parse(start,DateTimeFormatter.ISO_LOCAL_DATE))
                    .le(TaskEntity::getCreateTime,LocalDate.parse(end,DateTimeFormatter.ISO_LOCAL_DATE));
        }
        //根据最后更新时间倒序排序
        qw.orderByDesc(TaskEntity::getUpdateTime);

        return Pager.build(this.page(page,qw));
    }

    /**
     * 创建工单
     * @param taskViewModel
     * @return
     */
    @Override
    @Transactional
    public void addTask(TaskViewModel taskViewModel) throws LoginException {

        if (hasTask(taskViewModel.getInnerCode(), taskViewModel.getProductType())) {
            throw new LoginException("该机器有未完成的同类型工单");
        }
        // 判断机器状态
        VmVO vmVO = vmService.getVMInfo(taskViewModel.getInnerCode());
        checkout(taskViewModel ,vmVO);

        // 创建运维和运营工单基本信息
        TaskEntity task = new TaskEntity();
        task.setTaskCode(getTaskCode());
        task.setTaskStatus(VMSystem.TASK_STATUS_CREATE);
        task.setCreateType(taskViewModel.getCreateType());
        task.setInnerCode(taskViewModel.getInnerCode());
        task.setDesc(taskViewModel.getDesc());
        task.setProductTypeId(taskViewModel.getProductType());
        task.setUserId(taskViewModel.getUserId());
        task.setRegionId(vmVO.getRegionId());

        UserVO user = userService.getUser(taskViewModel.getUserId());
        if (user == null) {
            throw new LoginException("执行人不存在");
        }
        task.setUserName(user.getUserName());
        // 员工手里的工单数加一
        updateTaskZSet(task ,1);
        save(task);

        // 设置补货信息
        if (taskViewModel.getProductType() == VMSystem.TASK_TYPE_SUPPLY) {
            List<TaskDetailsViewModel> details = taskViewModel.getDetails();
            if (CollectionUtils.isEmpty(details)) {
                throw new LoginException("没有补货信息");
            }
            for (TaskDetailsViewModel detail : details) {
                TaskDetailsEntity taskDetailsEntity = new TaskDetailsEntity();
                BeanUtils.copyProperties(detail, taskDetailsEntity);
                taskDetailsEntity.setTaskId(task.getTaskId());
                taskDetailsService.save(taskDetailsEntity);
            }
        }
    }

    /**
     * 接受工单
     *
     * @param id
     * @return
     */
    @Override
    public boolean accept(Long id) throws LoginException {
        // 查询工单
        TaskEntity task = getById(id);
        if (task.getTaskStatus() != VMSystem.TASK_STATUS_CREATE) {
            throw new LoginException("工单状态不是待处理");
        }
        // 修改工单为进行状态
        task.setTaskStatus(VMSystem.TASK_STATUS_PROGRESS);
        return updateById(task);
    }

    /**
     * 取消工单
     * @param id
     * @return
     */
    @Override
    public boolean cancel(long id , CancelTaskViewModel cancel) throws LoginException {
        // 查询工单
        TaskEntity task = getById(id);
        if (task.getTaskStatus() != VMSystem.TASK_STATUS_PROGRESS) {
            throw new LoginException("工单状态不为进行");
        }
        // 修改工单状态为取消
        task.setTaskStatus(VMSystem.TASK_STATUS_CANCEL);
        // 添加取消工单原因
        task.setDesc(cancel.getDesc());
        // 员工手里的工单数减一
        updateTaskZSet(task, -1);
        return updateById(task);
    }

    /**
     * 完成工单
     *
     * @param id
     * @return
     * @throws LoginException
     */
    @Override
    public boolean finish(Long id) throws LoginException {
        TaskEntity task = getById(id);
        if (task.getTaskStatus() == VMSystem.TASK_STATUS_CANCEL || task.getTaskStatus() == VMSystem.TASK_STATUS_FINISH) {
            throw new LoginException("工单已完成或已取消");
        }
        task.setTaskStatus(VMSystem.TASK_STATUS_FINISH);
        updateById(task);

        // 如果是补货工单完成，发送消息到EMQ
        if (task.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY) {
            SupplyContract supplyContract = new SupplyContract();
            supplyContract.setInnerCode(task.getInnerCode());
            List<TaskDetailsEntity> list = taskDetailsService.getByTaskId(task.getTaskId());
            List<SupplyChannel> channelList = new ArrayList<>();
            for (TaskDetailsEntity taskDetails : list) {
                SupplyChannel supplyChannel = new SupplyChannel();
                supplyChannel.setChannelId(taskDetails.getChannelCode());
                supplyChannel.setCapacity(taskDetails.getExpectCapacity());
                supplyChannel.setSkuId(taskDetails.getSkuId());
                supplyChannel.setSkuImage(taskDetails.getSkuImage());
                supplyChannel.setSkuName(taskDetails.getSkuName());
                channelList.add(supplyChannel);
            }
            supplyContract.setSupplyData(channelList);
            try {
                mqttProducer.send(TopicConfig.VMS_SUPPLY_TOPIC, 1, supplyContract);
            } catch (JsonProcessingException e) {
                log.info("消息发送失败", e);
            }
        }
        // 投放或撤机工单完成，发送消息到EMQ
        if (task.getProductTypeId() == VMSystem.TASK_TYPE_DEPLOY || task.getProductTypeId() == VMSystem.TASK_TYPE_REVOKE) {
            TaskCompleteContract contract = new TaskCompleteContract();
            contract.setTaskType(task.getProductTypeId());
            contract.setInnerCode(task.getInnerCode());
            try {
                mqttProducer.send(TopicConfig.VMS_COMPLETED_TOPIC, 1, contract);
            } catch (JsonProcessingException e) {
                log.info("消息发送失败", e);
            }
        }
        return true;
    }

    /**
     * 获取同一天内分配的工单最少的人
     * @param regionId 区域id
     * @param isRepair 是否是运维工单
     * @return
     */
    @Override
    public int getLeastUser(Long regionId, Boolean isRepair) throws LoginException {
        // 运维人员编号
        String roleCode = VMSystem.USER_OPERATOR;
        if (isRepair) {
            // 运营人员编号
            roleCode = VMSystem.USER_REPAIRER;
        }
        String key = VMSystem.REGION_TASK_KEY_PREF + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) +
                "." + regionId + "." + roleCode;
        Set<Object> range = redisTemplate.opsForZSet().range(key, 0, 1);
        if (range == null || range.isEmpty()) {
            throw new LoginException("该区域暂时没有相关人员");
        }
        return (int) range.stream().collect(Collectors.toList()).get(0);
    }

    /**
     * 校验机器状态
     * @param taskViewModel
     * @param vmVO
     */
    private void checkout(TaskViewModel taskViewModel, VmVO vmVO) throws LoginException {
        VmVO vmInfo = vmService.getVMInfo(taskViewModel.getInnerCode());
        if (vmInfo == null) throw new LoginException("设备校验失败");
        // 如果是投放工单，状态为运营，抛出异常
        if (taskViewModel.getProductType() == VMSystem.TASK_TYPE_DEPLOY && vmVO.getVmStatus() == VMSystem.VM_STATUS_RUNNING) {
            throw new LoginException("该售货机为运营状态，不能投放");
        }
        // 补货工单，状态不是运营状态，抛出异常
        if (taskViewModel.getProductType() == VMSystem.TASK_TYPE_SUPPLY && vmVO.getVmStatus() != VMSystem.VM_STATUS_RUNNING) {
            throw new LoginException("该售货机不为运营状态，不能补货");
        }
        // 撤机工单，状态不是运营状态，抛出异常
        if (taskViewModel.getProductType() == VMSystem.TASK_TYPE_REVOKE && vmVO.getVmStatus() != VMSystem.VM_STATUS_RUNNING) {
            throw new LoginException("该售货机不为运营状态，不能撤机");
        }
    }

    /**
     * 生成工单编号
     * @return
     */
    private String getTaskCode() {
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String key = "lkd.task.code." + date;
        Object obj = redisTemplate.opsForValue().get(key);
        if (obj == null) {
            redisTemplate.opsForValue().setIfAbsent(key, 0L , Duration.ofDays(1));
        }
        return date + String.format("%04d", redisTemplate.opsForValue().increment(key,1));
    }
    /**
     * 同一台设备下是否存在未完成的工单
     * @param innerCode
     * @param productionType
     * @return
     */
    private boolean hasTask(String innerCode,int productionType){
        QueryWrapper<TaskEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .select(TaskEntity::getTaskId)
                .eq(TaskEntity::getInnerCode,innerCode)
                .eq(TaskEntity::getProductTypeId,productionType)
                .le(TaskEntity::getTaskStatus,VMSystem.TASK_STATUS_PROGRESS);
        return this.count(qw) > 0;
    }

    /**
     * 更新工单量列表
     * @param task
     * @param score 增加的分值
     */
    private void updateTaskZSet(TaskEntity task ,int score) {
        // 运维人员
        String roleCode = VMSystem.USER_REPAIRER;
        if (task.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY) {
            roleCode = VMSystem.USER_OPERATOR; // 运营人员
        }
        String key = VMSystem.REGION_TASK_KEY_PREF + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) +
                "." + task.getRegionId() + "." + roleCode;
        redisTemplate.opsForZSet().incrementScore(key ,task.getUserId() , score);
    }
}
